#!/usr/bin/env bash

# RunPanel 一键安装脚本
# - 零交互/无感知安装：自动检测系统与架构、拉取并校验发布包、创建最小权限服务并启动
# - 安全稳健：sha256 校验、最小权限 systemd、加固单元、可回退清理
# - 兼容多发行版：Debian/Ubuntu、RHEL/CentOS/Rocky/Alma、Fedora、Arch、Alpine
# - 支持版本钉死：RUNPANEL_VERSION="v1.2.3" bash install.sh
# - 可选参数（均为可选，默认即可）:
#     --version <vX.Y.Z|latest>  指定版本（默认 latest）
#     --with-incus               若检测到支持，将自动安装并初始化 Incus（可选）

set -Eeuo pipefail
umask 022

#--------------------------- UI & logging ---------------------------#
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; DIM='\033[2m'; NC='\033[0m'
msg() { echo -e "$1"; }
log_info() { msg "${GREEN}[INFO]${NC} $*"; }
log_warn() { msg "${YELLOW}[WARN]${NC} $*"; }
log_err() { msg "${RED}[ERROR]${NC} $*"; }
log_step() { msg "${BLUE}[STEP]${NC} $*"; }

#--------------------------- Globals ---------------------------#
INSTALL_DIR="/opt/runpanel"
BIN_NAME="runpanel"
SYMLINK_RP="/usr/local/bin/rp"
SERVICE_NAME="runpanel"
TMP_DIR="/tmp/runpanel-install.$(date +%s).$$"
VERSION="${RUNPANEL_VERSION:-}"
WITH_INCUS=false
# 自动检测CDN基础URL，支持自定义域名
CDN_BASE="https://get.runpanel.dev/releases"
GH_REPO="Run-Panel/RunPanel"                     # GitHub Fallback

cleanup() { rm -rf "$TMP_DIR" 2>/dev/null || true; }
trap cleanup EXIT

#--------------------------- Helpers ---------------------------#
require_root() {
    if [ "$(id -u)" -ne 0 ]; then
        log_err "此脚本必须以 root 运行。"
        exit 1
    fi
}

detect_os() {
    if [ -f /etc/os-release ]; then 
        # 保存当前的 VERSION 变量，避免被系统环境变量覆盖
        local SAVED_VERSION="$VERSION"
        . /etc/os-release
        OS_ID="$ID"; OS_NAME="$NAME"
        # 恢复 VERSION 变量
        VERSION="$SAVED_VERSION"
    else 
        log_err "无法检测操作系统 (缺少 /etc/os-release)"; exit 1; 
    fi
    case "$OS_ID" in
        ubuntu|debian)   OS_FAMILY=debian; PKG_UPDATE="apt-get update -y"; PKG_INSTALL="apt-get install -y" ;;
        centos|rhel|rocky|almalinux)
            if command -v dnf >/dev/null 2>&1; then OS_FAMILY=rhel; PKG_UPDATE="dnf -y update"; PKG_INSTALL="dnf -y install"; else OS_FAMILY=rhel; PKG_UPDATE="yum -y update"; PKG_INSTALL="yum -y install"; fi ;;
        fedora)          OS_FAMILY=fedora; PKG_UPDATE="dnf -y update"; PKG_INSTALL="dnf -y install" ;;
        arch|archlinux)  OS_FAMILY=arch;   PKG_UPDATE="pacman -Sy --noconfirm"; PKG_INSTALL="pacman -S --noconfirm" ;;
        alpine)          OS_FAMILY=alpine; PKG_UPDATE="apk update"; PKG_INSTALL="apk add --no-cache" ;;
        *) log_err "不支持的系统: $OS_NAME ($OS_ID)"; exit 1 ;;
    esac
    log_info "系统: $OS_NAME ($OS_FAMILY)"
}

detect_arch() {
    local m; m=$(uname -m)
    case "$m" in
        x86_64|amd64) ARCH=amd64 ;;
        aarch64|arm64) ARCH=arm64 ;;
        *) log_err "不支持的架构: $m（仅支持 amd64/arm64）"; exit 1 ;;
    esac
    log_info "架构: $ARCH"
}

install_deps() {
    log_step "安装依赖 (curl, tar, openssl, ca-certificates, jq, systemd 组件可选)"
    case "$OS_FAMILY" in
        debian)
            eval "$PKG_UPDATE" >/dev/null
            eval "$PKG_INSTALL curl tar openssl ca-certificates jq gnupg" >/dev/null
            ;;
        rhel|fedora)
            eval "$PKG_UPDATE" >/dev/null || true
            eval "$PKG_INSTALL curl tar openssl ca-certificates jq gnupg2 || $PKG_INSTALL gnupg" >/dev/null
            ;;
        arch)
            eval "$PKG_UPDATE" >/dev/null
            eval "$PKG_INSTALL curl tar openssl ca-certificates jq" >/dev/null
            ;;
        alpine)
            eval "$PKG_UPDATE" >/dev/null
            eval "$PKG_INSTALL curl tar openssl ca-certificates jq" >/dev/null
            ;;
    esac
    if command -v update-ca-trust >/dev/null 2>&1; then update-ca-trust >/dev/null || true; fi
}

parse_args() {
    while [ $# -gt 0 ]; do
        case "$1" in
            --version|-v) VERSION="${2:-}"; shift 2;;
            --with-incus) WITH_INCUS=true; shift;;
            --help|-h)
                cat <<EOF
用法: curl -fsSL https://get.runpanel.dev/install.sh | bash -s -- [选项]
选项:
    --version, -v <vX.Y.Z|latest>  指定安装版本（默认 latest）
    --with-incus                   安装并初始化 Incus（若支持）
    --help, -h                     显示帮助
EOF
                exit 0;;
            *) log_warn "忽略未知参数: $1"; shift;;
        esac
    done
    VERSION="${VERSION:-latest}"
}

ensure_dirs() { mkdir -p "$TMP_DIR" "$INSTALL_DIR" "$INSTALL_DIR/data" "$INSTALL_DIR/logs"; }

get_latest_tag_from_github() {
    # 返回最新发布的 tag（vX.Y.Z）
    local api="https://api.github.com/repos/${GH_REPO}/releases/latest"
    local tag response curl_exit_code
    
    # 先检查网络连接
    if ! curl -fsSL --connect-timeout 5 "https://api.github.com" >/dev/null 2>&1; then
        log_warn "无法连接到 GitHub API，可能网络有问题"
        return 1
    fi
    
    # 获取完整响应，关闭 pipefail 以便正确处理错误
    set +e
    response=$(curl -fsSL --connect-timeout 10 --retry 2 "$api" 2>/dev/null)
    curl_exit_code=$?
    set -e
    
    if [ $curl_exit_code -ne 0 ] || [ -z "$response" ]; then
        log_warn "GitHub API 请求失败 (退出码: $curl_exit_code)"
        return 1
    fi
    
    # 检查是否有错误信息
    if echo "$response" | jq -e '.message' >/dev/null 2>&1; then
        local error_msg
        error_msg=$(echo "$response" | jq -r '.message' 2>/dev/null || "未知错误")
        log_warn "GitHub API 错误: $error_msg"
        return 1
    fi
    
    # 解析 tag_name
    tag=$(echo "$response" | jq -r '.tag_name' 2>/dev/null)
    
    if [ -n "$tag" ] && [ "$tag" != "null" ]; then
        echo "$tag"
        return 0
    else
        log_warn "无法从 GitHub API 响应中解析版本号"
        return 1
    fi
}

resolve_version_and_urls() {
    # 确定版本与下载 URL；首选 CDN，失败则回退 GitHub Releases
    if [ "$VERSION" = "latest" ]; then
        log_step "获取最新版本信息..."
        RESOLVED_VER=$(get_latest_tag_from_github)
        if [ -z "$RESOLVED_VER" ]; then 
            log_err "无法解析最新版本，请检查网络或GitHub API速率限制"
            log_err "API URL: https://api.github.com/repos/${GH_REPO}/releases/latest"
            exit 1
        fi
        log_info "检测到最新版本: $RESOLVED_VER"
    else
        RESOLVED_VER="$VERSION"
        log_info "使用指定版本: $RESOLVED_VER"
    fi

    # 验证版本格式
    if ! echo "$RESOLVED_VER" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+'; then
        log_err "版本格式错误: $RESOLVED_VER (期望格式: vX.Y.Z)"
        exit 1
    fi

    TARBALL_NAME="runpanel-embedded-linux-${ARCH}.tar.gz"
    CHECKSUMS_NAME="checksums.txt"

    # CDN 首选
    CDN_TARBALL_URL="${CDN_BASE}/${RESOLVED_VER}/${TARBALL_NAME}"
    CDN_SUMS_URL="${CDN_BASE}/${RESOLVED_VER}/${CHECKSUMS_NAME}"

    # GitHub 备用
    GH_BASE="https://gh-proxy.com/https://github.com/${GH_REPO}/releases/download/${RESOLVED_VER}"
    GH_TARBALL_URL="${GH_BASE}/${TARBALL_NAME}"
    GH_SUMS_URL="${GH_BASE}/${CHECKSUMS_NAME}"
    
    log_info "下载链接已生成"
}

try_download() {
    local url="$1" out="$2"
    local http_code
    
    # 显示下载信息
    log_info "正在下载: $(basename "$url")"
    
    # 使用 --write-out 获取 HTTP 状态码
    http_code=$(curl -fL --connect-timeout 10 --retry 3 --retry-delay 2 \
        --write-out "%{http_code}" --silent --show-error \
        "$url" -o "$out" 2>/dev/null | tail -n1)
    
    if [ "$http_code" = "200" ] && [ -f "$out" ] && [ -s "$out" ]; then
        return 0
    else
        log_warn "下载失败: $url (HTTP: $http_code)"
        rm -f "$out" 2>/dev/null || true
        return 1
    fi
}

download_artifacts() {
    log_step "下载发布包（版本: ${RESOLVED_VER}）"
    local tarball="$TMP_DIR/${TARBALL_NAME}"
    local sums="$TMP_DIR/${CHECKSUMS_NAME}"

    if ! try_download "$CDN_TARBALL_URL" "$tarball"; then
        log_warn "CDN 不可用，回退 GitHub: $GH_TARBALL_URL"
        try_download "$GH_TARBALL_URL" "$tarball" || { log_err "下载二进制失败"; exit 1; }
    fi

    if ! try_download "$CDN_SUMS_URL" "$sums"; then
        log_warn "CDN checksums 不可用，回退 GitHub: $GH_SUMS_URL"
        try_download "$GH_SUMS_URL" "$sums" || { log_err "下载校验文件失败"; exit 1; }
    fi

    TARBALL_PATH="$tarball"; SUMS_PATH="$sums"
    log_info "下载完成: $(du -h "$TARBALL_PATH" | awk '{print $1}')"
}

verify_checksum() {
    log_step "校验文件完整性 (sha256)"
    local fname sha line
    fname=$(basename "$TARBALL_PATH")
    sha=$(sha256sum "$TARBALL_PATH" | awk '{print $1}')
    if ! grep -q "$fname" "$SUMS_PATH"; then log_err "checksums.txt 中不存在 $fname"; exit 1; fi
    line=$(grep "$fname" "$SUMS_PATH")
    expected=$(echo "$line" | awk '{print $1}')
    if [ "$sha" != "$expected" ]; then
        log_err "sha256 校验失败: expected=$expected actual=$sha"
        exit 1
    fi
    log_info "校验通过"
}

create_user_and_permissions() {
    log_step "设置目录权限 (root 运行)"
    chown -R root:root "$INSTALL_DIR"
}

unpack_to_install_dir() {
    log_step "解压并部署"
    local work="$TMP_DIR/unpack"
    mkdir -p "$work"
    tar -xzf "$TARBALL_PATH" -C "$work"
    # 期望结构: runpanel-embedded-linux-ARCH/{runpanel,configs,start.sh,...}
    local inner
    inner=$(find "$work" -maxdepth 1 -type d -name "runpanel-embedded-*" | head -1)
    if [ -z "$inner" ]; then log_err "压缩包结构异常"; exit 1; fi

    # 同步到 INSTALL_DIR（避免依赖 rsync）
    cp -a "$inner/." "$INSTALL_DIR/"
    chmod +x "$INSTALL_DIR/$BIN_NAME" "$INSTALL_DIR/start.sh" 2>/dev/null || true
}

install_symlink() {
    ln -sf "$INSTALL_DIR/$BIN_NAME" "$SYMLINK_RP"
}

create_systemd_service() {
    if ! command -v systemctl >/dev/null 2>&1; then
        log_warn "未检测到 systemd，跳过服务安装。可使用: $INSTALL_DIR/start.sh 启动"
        return
    fi

    log_step "创建并加固 systemd 服务单元"
    local unit="/etc/systemd/system/${SERVICE_NAME}.service"
    cat > "$unit" <<EOF
[Unit]
Description=RunPanel Server
Documentation=https://github.com/Run-Panel/RunPanel
After=network.target
Wants=network-online.target

[Service]
Type=simple
User=root
Group=root
WorkingDirectory=${INSTALL_DIR}
ExecStart=${INSTALL_DIR}/${BIN_NAME} -config ${INSTALL_DIR}/configs/config.yaml
ExecReload=/bin/kill -HUP \$MAINPID
Restart=on-failure
RestartSec=3s
TimeoutStopSec=20s
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=${INSTALL_DIR}
CapabilityBoundingSet=
AmbientCapabilities=
LockPersonality=true
MemoryDenyWriteExecute=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
SystemCallArchitectures=native
SystemCallFilter=@system-service

[Install]
WantedBy=multi-user.target
EOF

    systemctl daemon-reload
    systemctl enable "$SERVICE_NAME" >/dev/null 2>&1 || true
}

maybe_install_incus() {
    $WITH_INCUS || return 0
    log_step "可选: 安装 Incus (若支持)"
    if command -v incus >/dev/null 2>&1; then log_info "Incus 已存在: $(incus version 2>/dev/null | head -1)"; return 0; fi
    case "$OS_FAMILY" in
        debian)
            curl -fsSL https://pkgs.zabbly.com/release.asc | gpg --dearmor -o /usr/share/keyrings/zabbly.gpg || true
            echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/zabbly.gpg] https://pkgs.zabbly.com/incus/stable $(. /etc/os-release; echo $VERSION_CODENAME) main" > /etc/apt/sources.list.d/zabbly-incus-stable.list
            apt-get update -y && apt-get install -y incus || log_warn "Incus 安装失败（已忽略）"
            ;;
        rhel|fedora|arch|alpine)
            log_warn "当前发行版未内置自动安装 Incus，已跳过。"
            ;;
    esac
}

try_init() {
    log_step "初始化 (可选，失败不阻塞)"
    if ! env PATH="/usr/local/bin:/usr/bin:/bin" "$SYMLINK_RP" init >/dev/null 2>&1; then
        log_warn "rp init 可能需要交互，已跳过（可稍后运行: rp init）"
    fi
}

start_service_or_run() {
    if command -v systemctl >/dev/null 2>&1; then
        systemctl restart "$SERVICE_NAME" || true
        sleep 1
        if systemctl is-active --quiet "$SERVICE_NAME"; then
            log_info "服务已启动: systemctl status ${SERVICE_NAME}"
        else
            log_warn "服务未成功启动。可查看: journalctl -u ${SERVICE_NAME} -e"
        fi
    else
        log_info "使用脚本前台运行: ${INSTALL_DIR}/start.sh"
    fi
}

get_port_from_config() {
    local cfg="${INSTALL_DIR}/configs/config.yaml"
    [ -f "$cfg" ] || { echo 8081; return; }
    awk '/^server:/ {inserver=1} inserver==1 && /port:/ {print $2; exit}' "$cfg" 2>/dev/null || echo 8081
}

print_summary() {
    local port; port="$(get_port_from_config)"; port="${port:-8081}"
    echo ""
    echo "========================================"
    log_info "RunPanel 安装完成"
    echo "========================================"
    echo "位置: ${INSTALL_DIR}"
    echo "命令: rp (链接到 ${INSTALL_DIR}/${BIN_NAME})"
    echo "访问: http://localhost:${port}"
    if command -v systemctl >/dev/null 2>&1; then
        echo "服务: systemctl status ${SERVICE_NAME}"
    else
        echo "启动: ${INSTALL_DIR}/start.sh"
    fi
    echo "安全: 以 root 运行并启用 systemd 加固"
}

#--------------------------- Main ---------------------------#
main() {
    log_step "RunPanel 安装开始"
    parse_args "$@"
    require_root
    detect_os
    detect_arch
    install_deps
    ensure_dirs
    resolve_version_and_urls
    download_artifacts
    verify_checksum
    unpack_to_install_dir
    create_user_and_permissions
    install_symlink
    create_systemd_service
    maybe_install_incus || true
    try_init || true
    start_service_or_run
    print_summary
}

main "$@"
